package cache

import (
    "fmt"
    "sync"
    "time"
)

// ----------------------------------------
//  Redis 锁
// ----------------------------------------
type RedisLocker struct {
    client     *RedisClient
    expiration time.Duration
    retries    int
    interval   time.Duration
    key        string
    mu         *sync.Mutex
}

func CreateRedisLocker(client *RedisClient, key string, args ...interface{}) *RedisLocker {
    if len(args) > 0 {
        key = fmt.Sprintf(key, args...)
    }
    return &RedisLocker{
        client:     client,
        expiration: time.Second * 30,      // 默认 30 秒有效期
        retries:    0,                     // 默认不进行重试
        interval:   time.Millisecond * 20, // 默认重试间隔 20 毫秒
        key:        key,
        mu:         &sync.Mutex{},
    }
}

func (locker *RedisLocker) Key() string {
    return locker.key
}

func (locker *RedisLocker) GetExpiration() time.Duration {
    return locker.expiration
}

func (locker *RedisLocker) SetExpiration(expiration time.Duration) *RedisLocker {
    if expiration < 0 {
        expiration = 0
    }
    locker.expiration = expiration
    return locker
}

func (locker *RedisLocker) GetRetries() int {
    return locker.retries
}

func (locker *RedisLocker) SetRetries(retries int) *RedisLocker {
    if retries < 0 {
        retries = 0
    }
    locker.retries = retries
    return locker
}

func (locker *RedisLocker) SetInterval(interval time.Duration) *RedisLocker {
    if min := time.Millisecond * 5; interval < min {
        interval = min
    }
    locker.interval = interval
    return locker
}

func (locker *RedisLocker) Touch(duration time.Duration) error {
    return locker.client.Client.Expire(locker.key, duration).Err()
}

func (locker *RedisLocker) TouchAt(t time.Time) error {
    return locker.client.Client.ExpireAt(locker.key, t).Err()
}

func (locker *RedisLocker) Lock() bool {
    locker.mu.Lock()
    defer locker.mu.Unlock()
    client := locker.client.Client
    for i := 0; i <= locker.retries; i++ {
        if ok, err := client.SetNX(locker.key, 1, locker.expiration).Result(); err == nil && ok {
            return true
        }
        time.Sleep(locker.interval)
    }
    return false
}

func (locker *RedisLocker) DoLock() (ok bool, err error) {
    locker.mu.Lock()
    defer locker.mu.Unlock()
    client := locker.client.Client
    for i := 0; i <= locker.retries; i++ {
        ok, err := client.SetNX(locker.key, 1, locker.expiration).Result()
        if err == nil && ok {
            return true, nil
        }
        time.Sleep(locker.interval)
    }
    ok = false
    return
}

func (locker *RedisLocker) Unlock() bool {
    locker.mu.Lock()
    defer locker.mu.Unlock()
    client := locker.client.Client
    for i := 0; i <= locker.retries; i++ {
        if err := client.Del(locker.key).Err(); err == nil {
            return true
        }
        time.Sleep(locker.interval)
    }
    return false
}

func (locker *RedisLocker) DoUnlock() (bool, error) {
    locker.mu.Lock()
    defer locker.mu.Unlock()
    client := locker.client.Client
    var err error
    for i := 0; i <= locker.retries; i++ {
        err = client.Del(locker.key).Err()
        if err == nil {
            return true, nil
        }
        time.Sleep(locker.interval)
    }
    return false, err
}
